package org.xc.uvc_camera.encoder;

import org.xc.uvc_camera.FileUtil;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import example.sszpf.x264.x264sdk;

/**
 * yuv420sp码流转换为编码为h264
 */
public class H264Encoder {

    private H264Encoder() {}
    private static H264Encoder instance;
    public synchronized static H264Encoder getInstance() {
        if (instance == null) {
            instance = new H264Encoder();
        }
        return instance;
    }

    private x264sdk x264sdk;

    private int width,height;
    private int fps = 10;
    private int timeSpan = 90000 / fps;

    private long time;
    public boolean isRecord = false;

    //保存文件
    private FileOutputStream fos;
    private String currFileName;

    public void start(String fileName, int width, int height,IOnEncoderCallback callback) {
        close();

        this.currFileName = fileName;
        this.width = width;
        this.height = height;
        this.onEncoderCallback = callback;

        x264sdk = new x264sdk(listener);
        x264sdk.initX264Encode(width,height,fps,width * height);
        try {
            fos = new FileOutputStream(FileUtil.createFile(currFileName + ".h264"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        this.isRecord = true;
    }

    public void write(byte[] yuv420sp) {
        //本身初始化完毕 并且 录音也准备完毕，可以写入视频数据了
        if (isRecord && AacEncoder.getInstance().isRecord) {
            time += timeSpan;
            byte[] yuv420 = new byte[width * height * 3 / 2];
            YUV420SP2YUV420(yuv420sp,yuv420,width,height);
            x264sdk.PushOriStream(yuv420, yuv420.length, time);
        }
    }

    //h264数据回调
    private x264sdk.listener listener = new x264sdk.listener() {
        @Override
        public void h264data(byte[] buffer, int length) {
            if (isRecord) {
                try {
                    fos.write(buffer,0,buffer.length);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    };

    //关闭
    public void close() {
        isRecord = false;
        if (fos != null) {
            try {
                fos.flush();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (onEncoderCallback != null) {
                onEncoderCallback.over(currFileName);
            }
        }

        //关闭编码器
        x264sdk.CloseX264Encode();
    }

    private void YUV420SP2YUV420(byte[] yuv420sp, byte[] yuv420, int width, int height) {
        if (yuv420sp == null ||yuv420 == null)return;
        int framesize = width * height;
        int i = 0, j = 0;
        //copy y
        for (i = 0; i < framesize; i++) {
            yuv420[i] = yuv420sp[i];
        }
        i = 0;
        for (j = 0; j < framesize / 2; j += 2) {
            yuv420[i + framesize * 5 / 4] = yuv420sp[j+framesize];
            i++;
        }
        i = 0;
        for(j = 1; j < framesize / 2;j += 2) {
            yuv420[i+framesize] = yuv420sp[j+framesize];
            i++;
        }
    }

    public interface IOnEncoderCallback{

        void over(String fileName);

    }

    private IOnEncoderCallback onEncoderCallback;
}
